"""
Contains Unit Tests for testing the TicHackToe application.

Django Unit Testing documentation can be found:
    https://docs.djangoproject.com/en/dev/topics/testing/

Mockito acts as a stubbing tool and comes from:
    http://code.google.com/p/mockito-python/
"""

# from mockito import mock

from django.contrib.auth.models import User
from django.test import TestCase

from THT.models import THTGame, UserRecord, GameStatus
import time
import sys

class SimpleTest(TestCase):

    def make_game(self, players=True):
        game = THTGame.objects.create()
        try:
            player_x = User.objects.get(username=u'Player_X')
        except:
            player_x = User.objects.create_user(u'Player_X', u'x@test.com',
                                                u'player_x')
            player_x.userrecord_set.create()
        try:
            player_o = User.objects.get(username=u'Player_O')
        except:
            player_o = User.objects.create_user(u'Player_O', u'o@test.com',
                                                u'player_o')
            player_o.userrecord_set.create()
        if players:
            game.player_x_id = player_x.id
            game.player_o_id = player_o.id
        game.init_board()
        game.save()
        return game

    def test_player_x_username(self):
        game = self.make_game()
        username = game.player_x_username
        self.assertEqual(username, u'Player_X')
        
        game = THTGame.objects.create()
        username = game.player_x_username
        self.assertEqual(username, u'')
        
        game.player_x_id=1000000
        username = game.player_x_username
        self.assertEqual(username, u'')
        

    def test_player_o_username(self):
        game = self.make_game()
        username = game.player_o_username
        self.assertEqual(username, u'Player_O')
        
        game = THTGame.objects.create()
        username = game.player_o_username
        self.assertEqual(username, u'')
        
        game.player_o_id=1000000
        username = game.player_o_username
        self.assertEqual(username, u'')

    def test_check_activity(self):
        game = self.make_game()
        #game is not in progress, game status should not be effected
        status = game.game_status()
        game.check_activity()
        self.assertEqual(game.game_status(), status)
        #start game
        game.in_progress = 1
        game.last_move_ts = time.mktime(time.gmtime())
        game.save()
        self.assertEqual(game.game_status(), u'X turn')
        sys.stdout.write("\nWaiting for move timeout...\n")
        sys.stdout.flush()
        for i in range(65):
            if not i%11 or i == 59:
                self.assertEqual(game.game_status(), u'X turn')
            sys.stdout.write(".")
            sys.stdout.flush()
            time.sleep(1)
        print ("\n")
        game.check_activity()
        # game is reset upon X's loss
        self.assertEqual(game.game_status(), u'Game did not start')
        
        game = self.make_game()
        #start game
        game.in_progress = 1
        game.last_move_ts = time.mktime(time.gmtime())
        player_x = User.objects.get(username=u'Player_X')
        game.make_move(player_x, 'C')
        game.save()
        self.assertEqual(game.game_status(), u'O turn')
        sys.stdout.write("\nWaiting for move timeout...\n")
        sys.stdout.flush()
        for i in range(65):
            if not i%11 or i == 59:
                self.assertEqual(game.game_status(), u'O turn')
            sys.stdout.write(".")
            sys.stdout.flush()
            time.sleep(1)
        print ("\n")
        game.check_activity()
        # game is reset upon O's loss
        self.assertEqual(game.game_status(), u'Game did not start')
        #x did not move in the allotted time, game should be over & reset
        #self.assertEqual(game.game_status(), u'Game did not start')
        # O won the original game

        # Todo - fix the end of this test.
        #gameStatus = GameStatus.objects.get(game_id=game_id)
        #self.assertEqual(gameStatus.get_status(), u'O won!')
    
    def test_game_status(self):
        #Test not started game
        game = self.make_game()
        self.assertEqual(game.game_status(), u'Game did not start')
        
        #Start game
        game.in_progress = 1
        game.save()
        
        #Check turn
        self.assertEqual(game.game_status(), u'X turn')
        game.in_progress = 1
        game.last_move = u'X'
        game.last_move_ts = time.mktime(time.gmtime())
        game.save()
        self.assertEqual(game.game_status(), u'O turn')
       
        #Tie 
        game = self.make_game()
        game.in_progress = 1
        game.save()
        game.board = {'NW': u'O', 'N': u'X', 'NE': u'O',
                      'W': u'X', 'C': u'X', 'E': u'O',
                      'SW': u'X', 'S': u'O', 'SE': u'X'}
        self.assertEqual(game.game_status(), u'Tie')
        
        #X Won 
        game = self.make_game()
        game.in_progress = 1
        game.save()
        game.board = {'NW': u'O', 'N': u'X', 'NE': u'O',
                      'W': u'O', 'C': u'X', 'E': u'X',
                      'SW': u'X', 'S': u'X', 'SE': u'O'}
        self.assertEqual(game.game_status(), u'Game Over! X won')

    def test_get_available_moves(self):
        game = self.make_game()
        #Empty board
        print u'Checking empty board'
        moves = game.get_available_moves()
        moves.sort()
        available_moves = ['C', 'E', 'N', 'NE', 'NW', 'S', 'SE', 'SW', 'W']
        self.assertEqual(moves, available_moves)
        
        #Partial board
        print u'Checking partial board'
        game.board = {'NW': u'', 'N': u'X', 'NE': u'',
                      'W': u'X', 'C': u'X', 'E': u'X',
                      'SW': u'', 'S': u'X', 'SE': u''}
        moves = game.get_available_moves()
        moves.sort()
        available_moves = ['NE', 'NW', 'SE', 'SW']
        self.assertEqual(moves, available_moves)

        #Four corners
        print u'Checking partial board'
        game.board = {'NW': u'X', 'N': u'', 'NE': u'X',
                      'W': u'', 'C': u'', 'E': u'',
                      'SW': u'X', 'S': u'', 'SE': u'X'}
        moves = game.get_available_moves()
        moves.sort()
        available_moves = ['C', 'E', 'N', 'S', 'W']
        self.assertEqual(moves, available_moves)

        #Full board
        print u'Checking full board'
        game.board = {'NW': u'O', 'N': u'X', 'NE': u'O',
                      'W': u'O', 'C': u'X', 'E': u'X',
                      'SW': u'O', 'S': u'X', 'SE': u'X'}
        moves = game.get_available_moves()
        moves.sort()
        self.assertEqual(len(moves), 0)
        available_moves = []
        self.assertEqual(moves, available_moves)

    def test_make_move(self):
        game = self.make_game()
        player_x = User.objects.get(username=u'Player_X')
        player_o = User.objects.get(username=u'Player_O')
        self.assertEqual(player_x.id, game.player_x_id)
        self.assertEqual(player_o.id, game.player_o_id)
        game.init_board()
        #Test invalid position
        try:
            game.make_move(player_x, 'U')
        except ValueError:
            print 'Expected failure at invalid position'
            self.assertEqual(1,1)
        except e:
            self.fail('Unexpected exception thrown: ', e)
        else:
            self.fail('Expected exception not thrown') 
        
        #Test X make a move    
        game.make_move(player_x, 'C')
        self.assertEquals(game.board['C'], u'X') 
        self.assertEquals(game.last_move, u'X')
        self.assertEquals(len(game.get_available_moves()), 8)
        print 'X made a move'
        
        #Test occupied position
        try:
            game.make_move(player_o, 'C')
        except ValueError:
            print 'Expected failure at occupied position'
            self.assertEqual(1,1)
        except e:
            self.fail('Unexpected exception thrown: ', e)
        else:
            self.fail('Expected exception not thrown') 
        
        #Test not your turn to move
        try:
            game.make_move(player_x, 'NW')
        except ValueError:
            print 'Expected failure since it is not X\'s turn'
            self.assertEqual(1,1)
        except e:
            self.fail('Unexpected exception thrown: ', e)
        else:
            self.fail('Expected exception not thrown') 
            
        #Test o make a move
        game.make_move(player_o, 'NW')
        self.assertEquals(game.board['NW'], u'O') 
        self.assertEquals(game.last_move, u'O')
        self.assertEquals(len(game.get_available_moves()), 7)
        print 'O made a move'

    def test_quit_game(self):
        game = self.make_game()
        game.in_progress = True 
        game.save()

        quitter = User.objects.get(username=u'Player_X')
        self.assertEqual(game.player_x_id, quitter.id)
        game.quit_game(quitter)
        self.assertEqual(game.player_x_id,0)

        game = self.make_game()
        game.in_progress = True 
        game.save()
        quitter = User.objects.get(username=u'Player_O')
        self.assertEqual(game.player_o_id, quitter.id)
        game.quit_game(quitter)
        self.assertEqual(game.player_o_id,0)
        
        game = self.make_game()
        game.in_progress = True 
        game.save()
        quitter = User.objects.create_user(u'quitter', u'x@test.com',
                                                u'quitter')
        self.assertEqual(game.quit_game(quitter), None)

    def test_register_user(self):
        game = self.make_game(players=False)
        player_x = User.objects.get(username=u'Player_X')
        player_o = User.objects.get(username=u'Player_O')
        self.assertEqual(game.player_x_id,0)
        self.assertEqual(game.player_o_id,0)
        # Test sitting down at position X
        result = game.register_user(player_x.id, u'X')
        self.assertEqual(result, True)
        # Test sitting at same position
        result = game.register_user(player_x.id, u'X')
        self.assertEqual(result, True)
        # Test trying to sit where another player already is
        result = game.register_user(player_o.id, u'X')
        self.assertEqual(result, False)
        # Test trying to sit at an invalid position
        result = game.register_user(player_o.id, u'T')
        self.assertEqual(result, False)
        # Test sitting down at position O
        result = game.register_user(player_o.id, u'O')
        self.assertEqual(result, True)
        # Test sitting at same position
        result = game.register_user(player_o.id, u'O')
        self.assertEqual(result, True)
        # Test trying to sit where another player already is
        result = game.register_user(player_x.id, u'O')
        self.assertEqual(result, False)

    def test_check_win(self):
        game = self.make_game()
        result, outcome = game.check_win()
        self.assertEqual(result, False)
        self.assertEqual(outcome, u'')

        game.board = {'NW': u'', 'N': u'X', 'NE': u'',
                      'W': u'', 'C': u'X', 'E': u'',
                      'SW': u'', 'S': u'X', 'SE': u''}
        game.save()
        result, outcome = game.check_win()
        self.assertEqual(result, True)
        self.assertEqual(outcome, u'X')

        game = self.make_game()
        game.board = {'NW': u'', 'N': u'X', 'NE': u'',
                      'W': u'', 'C': u'O', 'E': u'',
                      'SW': u'', 'S': u'X', 'SE': u''}
        game.save()
        result, outcome = game.check_win()
        self.assertEqual(result, False)
        self.assertEqual(outcome, u'')

        game = self.make_game()
        game.board = {'NW': u'O', 'N': u'X', 'NE': u'',
                      'W': u'', 'C': u'O', 'E': u'',
                      'SW': u'', 'S': u'X', 'SE': u'O'}
        game.save()
        result, outcome = game.check_win()
        self.assertEqual(result, True)
        self.assertEqual(outcome, u'O')

        game = self.make_game()
        game.board = {'NW': u'O', 'N': u'X', 'NE': u'X',
                      'W': u'', 'C': u'X', 'E': u'',
                      'SW': u'X', 'S': u'X', 'SE': u'O'}
        game.save()
        result, outcome = game.check_win()
        self.assertEqual(result, True)
        self.assertEqual(outcome, u'X')

        game = self.make_game()
        game.board = {'NW': u'O', 'N': u'X', 'NE': u'',
                      'W': u'O', 'C': u'', 'E': u'',
                      'SW': u'O', 'S': u'X', 'SE': u'O'}
        game.save()
        result, outcome = game.check_win()
        self.assertEqual(result, True)
        self.assertEqual(outcome, u'O')

        game = self.make_game()
        game.board = {'NW': u'O', 'N': u'X', 'NE': u'O',
                      'W': u'X', 'C': u'', 'E': u'O',
                      'SW': u'O', 'S': u'X', 'SE': u'O'}
        game.save()
        result, outcome = game.check_win()
        self.assertEqual(result, True)
        self.assertEqual(outcome, u'O')

